/*
 * Copyright (C) 2013 Square, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.squareup.picasso;

import android.annotation.TargetApi;
import android.graphics.Bitmap;
import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;

import static android.os.Process.THREAD_PRIORITY_BACKGROUND;

class Stats {
	private static final int CACHE_HIT = 0;
	private static final int CACHE_MISS = 1;
	private static final int BITMAP_DECODE_FINISHED = 2;
	private static final int BITMAP_TRANSFORMED_FINISHED = 3;
	private static final int DOWNLOAD_FINISHED = 4;

	private static final String STATS_THREAD_NAME = Utils.THREAD_PREFIX
			+ "Stats";

	final HandlerThread statsThread;
	final Cache cache;
	final Handler handler;

	long cacheHits;
	long cacheMisses;
	long totalDownloadSize;
	long totalOriginalBitmapSize;
	long totalTransformedBitmapSize;
	long averageDownloadSize;
	long averageOriginalBitmapSize;
	long averageTransformedBitmapSize;
	int downloadCount;
	int originalBitmapCount;
	int transformedBitmapCount;

	Stats(Cache cache) {
		this.cache = cache;
		this.statsThread = new HandlerThread(STATS_THREAD_NAME,
				THREAD_PRIORITY_BACKGROUND);
		this.statsThread.start();
		this.handler = new StatsHandler(statsThread.getLooper(), this);
	}

	void dispatchBitmapDecoded(Bitmap bitmap) {
		processBitmap(bitmap, BITMAP_DECODE_FINISHED);
	}

	void dispatchBitmapTransformed(Bitmap bitmap) {
		processBitmap(bitmap, BITMAP_TRANSFORMED_FINISHED);
	}

	void dispatchDownloadFinished(long size) {
		handler.sendMessage(handler.obtainMessage(DOWNLOAD_FINISHED, size));
	}

	void dispatchCacheHit() {
		handler.sendEmptyMessage(CACHE_HIT);
	}

	void dispatchCacheMiss() {
		handler.sendEmptyMessage(CACHE_MISS);
	}

	@TargetApi(Build.VERSION_CODES.ECLAIR)
	void shutdown() {
		statsThread.quit();
	}

	void performCacheHit() {
		cacheHits++;
	}

	void performCacheMiss() {
		cacheMisses++;
	}

	void performDownloadFinished(Long size) {
		downloadCount++;
		totalDownloadSize += size;
		averageDownloadSize = getAverage(downloadCount, totalDownloadSize);
	}

	void performBitmapDecoded(long size) {
		originalBitmapCount++;
		totalOriginalBitmapSize += size;
		averageOriginalBitmapSize = getAverage(originalBitmapCount,
				totalOriginalBitmapSize);
	}

	void performBitmapTransformed(long size) {
		transformedBitmapCount++;
		totalTransformedBitmapSize += size;
		averageTransformedBitmapSize = getAverage(originalBitmapCount,
				totalTransformedBitmapSize);
	}

	StatsSnapshot createSnapshot() {
		return new StatsSnapshot(cache.maxSize(), cache.size(), cacheHits,
				cacheMisses, totalDownloadSize, totalOriginalBitmapSize,
				totalTransformedBitmapSize, averageDownloadSize,
				averageOriginalBitmapSize, averageTransformedBitmapSize,
				downloadCount, originalBitmapCount, transformedBitmapCount,
				System.currentTimeMillis());
	}

	private void processBitmap(Bitmap bitmap, int what) {
		// Never send bitmaps to the handler as they could be recycled before we
		// process them.
		int bitmapSize = Utils.getBitmapBytes(bitmap);
		handler.sendMessage(handler.obtainMessage(what, bitmapSize, 0));
	}

	private static long getAverage(int count, long totalSize) {
		return totalSize / count;
	}

	private static class StatsHandler extends Handler {

		private final Stats stats;

		public StatsHandler(Looper looper, Stats stats) {
			super(looper);
			this.stats = stats;
		}

		@Override
		public void handleMessage(final Message msg) {
			switch (msg.what) {
			case CACHE_HIT:
				stats.performCacheHit();
				break;
			case CACHE_MISS:
				stats.performCacheMiss();
				break;
			case BITMAP_DECODE_FINISHED:
				stats.performBitmapDecoded(msg.arg1);
				break;
			case BITMAP_TRANSFORMED_FINISHED:
				stats.performBitmapTransformed(msg.arg1);
				break;
			case DOWNLOAD_FINISHED:
				stats.performDownloadFinished((Long) msg.obj);
				break;
			default:
				Picasso.HANDLER.post(new Runnable() {
					@Override
					public void run() {
						throw new AssertionError("Unhandled stats message."
								+ msg.what);
					}
				});
			}
		}
	}
}